//! \file CMutex.cpp
// -----------------------------------------------------------------------------
//! This program is free software; you can redistribute it and/or
//! modify it under the terms of the GNU General Public License
//! as published by the Free Software Foundation; either version 2
//! of the License, or (at your option) any later version.
//!
//! This program is distributed in the hope that it will be useful,
//! but WITHOUT ANY WARRANTY; without even the implied warranty of
//! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//! GNU General Public License for more details.
//!
//! You should have received a copy of the GNU General Public License
//! along with this program; if not, write to the Free Software Foundation,
//! Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
// -----------------------------------------------------------------------------

// Project
#include "CMutex.h"

// Standard
#include <time.h>


namespace code_sheriff
{

// =====[ Public Methods ]======================================================


// =============================================================================
// CMutex::CMutex
// -----------------------------------------------------------------------------
CMutex::CMutex()
{
  // Create the system native mutex.
  pthread_mutex_init(&(m_Mutex), NULL);
}


// =============================================================================
// CMutex::~CMutex
// -----------------------------------------------------------------------------
CMutex::~CMutex()
{
  // Destroy the system native mutex.
  pthread_mutex_destroy(&(m_Mutex));
}


// =============================================================================
// CMutex::lock
// -----------------------------------------------------------------------------
TERROR CMutex::lock(tTIME_MS Timeout)
{
  struct timespec Time;

  // Determine how we've been requested to lock our mutex.
  if (Timeout == TIMEOUT_IMMEDIATELY)
  {
    // Try to lock it and don't block.
    return (pthread_mutex_trylock(&(m_Mutex)));
  }
  else if (Timeout == TIMEOUT_FOREVER)
  {
    // Lock it and block.
    return (pthread_mutex_lock(&(m_Mutex)));
  }
  else
  {
    // Lock it but only block for the requested Timeout interval.

    // Get the current time.
    clock_gettime(CLOCK_REALTIME, &(Time));

    // Add the timeout to define when the absolute timeout should occur.
    Time.tv_sec+= (Timeout / 1000);
    Time.tv_nsec+= (Timeout % 1000) * 1000000;

    // Prevent bad argument being from being sent to our native system call.
    // A EINVAL argument will occur if the abs_timeout parameter specified
    // has a nanoseconds field value less than zero or greater than or equal to
    // 1000 million.

    // Low value check of nanoseconds field value.
    do
    {
      Time.tv_sec-= 1;           // Detract one second.
      Time.tv_nsec+= 1000000000; // Add one second.
    } while (Time.tv_nsec < 0);

    // High value check of nanoseconds field value.
    do
    {
      Time.tv_sec+= 1;           // Add one second.
      Time.tv_nsec-= 1000000000; // Detract one second.
    } while (Time.tv_nsec >= 1000000000);

    // Finally attempt to the lock the mutex within our Timeout interval.
    return (pthread_mutex_timedlock(&(m_Mutex), &(Time)));
  }
}


// =============================================================================
// CMutex::unlock
// -----------------------------------------------------------------------------
TERROR CMutex::unlock(void)
{
  return (pthread_mutex_unlock(&(m_Mutex)));
}


} // namespace code_sheriff
